import { Injectable } from '@nestjs/common'
import { RoleService } from '../role/role.service'
import { InjectRepository } from '@nestjs/typeorm'
import { RedisService } from '@/shared/redis.service'
import { SharedService } from '@/shared/shared.service'
import { FindOptionsWhere, Like, Not, Repository } from 'typeorm'
import { ApiException, AuthEnum, CacheKey, CreateUserDTO, TimeUnit, UpdateProfileDTO, UpdateUserDTO, UpdateUserPwdDTO, UserEntity } from '@/common'

@Injectable()
export class UserService {
  constructor(
    private readonly roleService: RoleService,
    private readonly redisService: RedisService,
    private readonly sharedService: SharedService,
    @InjectRepository(UserEntity) private readonly userRepository: Repository<UserEntity>,
  ) {}

  /** 新建用户 */
  public async create(createDto: CreateUserDTO) {
    const record = await this.userRepository.findOneBy({ username: createDto.username })
    if (record) throw new ApiException('该用户已存在')
    const hasNickname = await this.userRepository.findOneBy({ nickname: createDto.nickname })
    if (hasNickname) throw new ApiException('该昵称已存在')
    const hasQQ = createDto.qq && (await this.userRepository.findOneBy({ qq: createDto.qq }))
    if (hasQQ) throw new ApiException('该QQ已被绑定')
    const hasPhone = createDto.phone && (await this.userRepository.findOneBy({ phone: createDto.phone }))
    if (hasPhone) throw new ApiException('该手机号码已被绑定')
    createDto.password = await this.sharedService.generateHashPwd(createDto.password) //  处理密码
    createDto.roles = await this.roleService.findManyByIds(createDto.roleIds) // 关联角色
    await this.userRepository.save(createDto)
    return `添加成功`
  }

  /** 根据传入 id 组删除单个或者多个数据 */
  public async delete(userIds: string[]) {
    const containsAdmin = userIds.includes(AuthEnum.ADMIN_USER_ID)
    if (containsAdmin) throw new ApiException(`管理员账号无法被删除`)
    await Promise.all(userIds.map((userId) => this.clearCacheByUserId(userId))) // 并行清除所有用户的缓存
    await this.userRepository.delete(userIds)
    return `删除成功`
  }

  /** 修改用户信息 */
  public async update(updateDto: UpdateUserDTO): Promise<string> {
    const hasNickname = updateDto.nickname && (await this.userRepository.findOneBy({ nickname: updateDto.nickname, id: Not(updateDto.id) }))
    if (hasNickname) throw new ApiException('该昵称已存在')
    const hasQQ = updateDto.qq && (await this.userRepository.findOneBy({ qq: updateDto.qq, id: Not(updateDto.id) }))
    if (hasQQ) throw new ApiException('该QQ已被绑定')
    const hasPhone = updateDto.phone && (await this.userRepository.findOneBy({ phone: updateDto.phone, id: Not(updateDto.id) }))
    if (hasPhone) throw new ApiException('该手机号码已被绑定')
    updateDto.roles = await this.roleService.findManyByIds(updateDto.roleIds)
    const roleCodeList = updateDto.roles.map((item) => item.code) // 将用户其对应的角色码存到 Redis
    await this.redisService.setEx(`${CacheKey.ADMIN_USER_ROLES}:${updateDto.id}`, JSON.stringify(roleCodeList), TimeUnit.DAY)
    await this.userRepository.save(updateDto)
    if (!updateDto.status) await this.clearCacheByUserId(updateDto.id) // 停用用户的同时清空所有权限缓存
    return '更新成功'
  }

  /** 修改用户个人信息 */
  public async updateProfile(userId: string, updateDto: UpdateProfileDTO) {
    const hasNickname = await this.userRepository.findOneBy({ nickname: updateDto.nickname, id: Not(userId) })
    if (hasNickname) throw new ApiException('该昵称已存在')
    const hasQQ = (await this.userRepository.findOneBy({ qq: updateDto.qq, id: Not(userId) })) && updateDto.qq
    if (hasQQ) throw new ApiException('该QQ已被绑定')
    const hasPhone = (await this.userRepository.findOneBy({ phone: updateDto.phone, id: Not(userId) })) && updateDto.phone
    if (hasPhone) throw new ApiException('该手机号码已被绑定')
    await this.userRepository.update(userId, updateDto)
    return '个人信息修改成功'
  }

  /** 查询用户分页列表 */
  public async findList(queryParams: TableQueryParams<UserEntity>) {
    const { username, phone, take, skip, gender, nickname, realname, status } = queryParams
    const queryBuilder = this.userRepository.createQueryBuilder('user')
    const keys: (keyof UserEntity)[] = ['id', 'username', 'nickname', 'phone', 'status', 'gender', 'realname', 'createTime', 'updateTime']
    keys.push('createBy', 'updateBy')
    queryBuilder.select(keys.map((key) => `user.${key}`))
    const where: FindOptionsWhere<UserEntity> = {}
    if (username) where.username = Like(`%${username}%`)
    if (nickname) where.nickname = Like(`%${nickname}%`)
    if (phone) where.phone = Like(`%${phone}%`)
    if (status) where.status = +status
    queryBuilder.where(where)
    queryBuilder.orderBy('user.createTime', 'ASC') // 排序
    queryBuilder.skip(skip).take(take) // 分页
    const [records, total] = await queryBuilder.getManyAndCount() //  一次性获取数据和总数
    return { total, records }
  }

  /** 根据用户 username 查找其详情 */
  public async findOneByUsername(username: string) {
    const user = await this.userRepository.findOneBy({ username })
    if (!user) throw new ApiException(`用户 ${username} 不存在`)
    return user
  }

  /** 根据用户 ID 查找其详情 */
  public async findOneById(userId: string) {
    const user = await this.userRepository.findOne({ where: { id: userId }, relations: ['roles'] })
    if (!user) throw new ApiException(`该用户不存在`)
    if (user.qq) user.avatar = `https://q1.qlogo.cn/g?b=qq&nk=${user.qq}&s=640&t=${Date.now()}`
    const roleIds = user.roles.map((role) => role.id) || [] // 提取角色 ID 数组
    return { ...user, roleIds }
  }

  /** 重置用户密码为系统默认值 */
  public async resetPassword(userId: string) {
    const user = await this.userRepository.findOneBy({ id: userId })
    if (!user) throw new ApiException('该用户不存在')
    const defaultPwd = await this.sharedService.generateHashPwd()
    await this.userRepository.update(userId, { password: defaultPwd })
    await this.clearCacheByUserId(userId)
    return `${user.realname} 的密码已重置成功`
  }

  /** 修改密码 */
  public async updatePassword(updateDto: UpdateUserPwdDTO) {
    const { oldPassword, newPassword, repeatPassword, userId } = updateDto
    if (newPassword !== repeatPassword) throw new ApiException(`两次输入的密码不一致`)
    const user = await this.userRepository.findOneBy({ id: userId })
    if (!user) throw new ApiException('该用户不存在')
    const isOldPasswordValid = await this.sharedService.validateHashPwd(user.password, oldPassword)
    if (!isOldPasswordValid) throw new ApiException(`旧密码输入错误`)
    const isSame = await this.sharedService.validateHashPwd(user.password, newPassword)
    if (isSame) throw new ApiException(`新密码不能与旧密码相同`)
    const newPwd = await this.sharedService.generateHashPwd(newPassword)
    await this.userRepository.update(userId, { password: newPwd })
    await this.clearCacheByUserId(userId)
    return '密码修改成功'
  }

  /** 清除与指定 userId 关联的所有缓存条目 */
  public async clearCacheByUserId(userId: string) {
    const keys = await this.redisService.keys(`*${userId}*`)
    if (!keys.length) return '未发现需要清除的用户缓存'
    await this.redisService.del(keys)
    return `成功清除 ${keys.length} 个相关缓存`
  }
}
